package com.minhang.config.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.minhang.config.security.filter.JwtFilter;
import com.minhang.config.security.filter.LoginFilter;
import com.minhang.domain.Employee;
import com.minhang.utils.JwtUtils;
import com.minhang.utils.result.Result;
import com.minhang.utils.result.ResultCode;
import com.minhang.utils.result.ResultGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * @author rookie
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtFilter jwtFilter;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 自定义加密使用默认加密
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();
    }

    /**
     * 授权
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        
        http.authorizeRequests()
                .antMatchers("/publicKey/**","/privateKey/**","/login/**", "/reLogin/**", "/modifyPwd/**", "/sendVerification/**","/sys/**","/ding/**","/get/**").permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
//                .antMatchers("/emp/update/**", "/skill/**", "/sys/**").hasAnyAuthority("admin", "user")
//                .antMatchers("/sys/**","/register/**","/dept/select/**","/post/select/**","/role/select/**","/skill/**").hasAnyAuthority("admin","user")
//                .antMatchers("/**/save/**", "/**/delete/**", "/**/update/**").hasAnyAuthority("admin")
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                //登出
                .logout()
                .logoutUrl("/logout")
                //退出
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        String token = httpServletRequest.getHeader("token");
                        redisTemplate.delete("OA:token:" + token);
                        PrintWriter out = httpServletResponse.getWriter();
                        out.write(new ObjectMapper().writeValueAsString(ResultGenerator.genSuccessResult()));
                        out.flush();
                        out.close();
                    }
                })
                .and()
                //权限验证失败
                .exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        out.write(new ObjectMapper().writeValueAsString(ResultGenerator.genFailResult("权限不足", ResultCode.AUTHORITY)));
                        out.flush();
                        out.close();
                    }
                })
                .and()
                .cors()//新加入
                .and()
                .csrf().disable();

        //登录拦截
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
        //从token中解析用户信息
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }


    /**
     * 跨域支持
     *
     * @return
     */
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }

    /**
     * 认证服务器
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    LoginFilter loginFilter() throws Exception {

        LoginFilter filter = new LoginFilter();
        filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                                                HttpServletResponse httpServletResponse,
                                                Authentication authentication) throws IOException, ServletException {

                httpServletResponse.setContentType("application/json;charset=utf-8");
                PrintWriter out = httpServletResponse.getWriter();
                //获取登录成功的对象
                Employee employee = (Employee) authentication.getPrincipal();
                //添加token负载
                Map<String, String> map = new HashMap<>();
                map.put("id", employee.getId().toString());
                map.put("name", employee.getName());
                map.put("email", employee.getEnterpriseEmail());
                //生成token
                String token = JwtUtils.getToken(map);
                //数据对象
                Map<String, Object> objectMap = new HashMap<>();
                objectMap.put("user", employee);
                objectMap.put("token", token);

                Map<String, String> emp = new HashMap<>();
                emp.put("id",employee.getId().toString());
                emp.put("pwd",employee.getPwd());

                redisTemplate.opsForValue().set("OA:token:" + token, emp, 120, TimeUnit.MINUTES);

                //发送
                out.write(new ObjectMapper().writeValueAsString(ResultGenerator.genSuccessResult(objectMap)));
                out.flush();
                out.close();
            }
        });
        filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                httpServletResponse.setContentType("application/json;charset=utf-8");
                PrintWriter out = httpServletResponse.getWriter();
                Result result = new Result();
                if (e instanceof LockedException) {
                    result.setMessage("账户被锁定，请联系管理员!");
                    result.setCode(ResultCode.FAILURE);
                } else if (e instanceof CredentialsExpiredException) {
                    result.setMessage("密码过期，请联系管理员!");
                    result.setCode(ResultCode.FAILURE);
                } else if (e instanceof AccountExpiredException) {
                    result.setMessage("账户过期，请联系管理员!");
                    result.setCode(ResultCode.FAILURE);
                } else if (e instanceof DisabledException) {
                    result.setMessage("账户被禁用，请联系管理员!");
                    result.setCode(ResultCode.FAILURE);
                } else if (e instanceof BadCredentialsException) {
                    result.setMessage("用户名或者密码输入错误，请重新输入!");
                    result.setCode(ResultCode.FAILURE);
                }
                out.write(new ObjectMapper().writeValueAsString(result));
                out.flush();
                out.close();
            }
        });
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setUsernameParameter("username");
        filter.setPasswordParameter("pwd");
        filter.setFilterProcessesUrl("/login");
        return filter;

    }



}
